package cn.initcap.controller;

import cn.initcap.access.AccessLimit;
import cn.initcap.common.ErrorCode;
import cn.initcap.common.Result;
import cn.initcap.entity.SeckillOrder;
import cn.initcap.entity.SeckillUser;
import cn.initcap.rabbitmq.MqSender;
import cn.initcap.rabbitmq.SeckillMessage;
import cn.initcap.redis.GoodsKey;
import cn.initcap.redis.OrderKey;
import cn.initcap.redis.RedisService;
import cn.initcap.redis.SeckillKey;
import cn.initcap.service.GoodsService;
import cn.initcap.service.OrderService;
import cn.initcap.service.SeckillService;
import cn.initcap.vo.GoodsVo;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author initcap
 * @date Created in 2018/8/10 PM5:44.
 */
@Slf4j
@Controller
@RequestMapping("/miaosha")
public class SeckillController implements InitializingBean {

    private RedisService redisService;
    private GoodsService goodsService;
    private OrderService orderService;
    private SeckillService seckillService;
    private MqSender sender;
    private HashMap<Long, Boolean> localOverMap = new HashMap<>();

    @Autowired
    public SeckillController(RedisService redisService, GoodsService goodsService, OrderService orderService,
                             SeckillService seckillService, MqSender sender) {
        this.redisService = redisService;
        this.goodsService = goodsService;
        this.orderService = orderService;
        this.seckillService = seckillService;
        this.sender = sender;
    }

    /**
     * 系统初始化
     *
     * @throws Exception 系统初始化产生异常
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        if (goodsList == null) {
            return;
        }
        for (GoodsVo goods : goodsList) {
            redisService.set(GoodsKey.GET_MIAOSHA_GOODS_STOCK, "" + goods.getId(), goods.getStockCount());
            localOverMap.put(goods.getId(), false);
        }
    }

    @GetMapping(value = "/reset")
    @ResponseBody
    public Result reset() {
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        for (GoodsVo goods : goodsList) {
            goods.setStockCount(10);
            redisService.set(GoodsKey.GET_MIAOSHA_GOODS_STOCK, "" + goods.getId(), 10);
            localOverMap.put(goods.getId(), false);
        }
        redisService.delete(OrderKey.GET_SECKILL_ORDER_BY_UID_GID);
        redisService.delete(SeckillKey.IS_GOODS_OVER);
        seckillService.reset(goodsList);
        return Result.success(true);
    }

    /**
     * QPS:1306
     * 5000 * 10
     * QPS: 2114
     */
    @PostMapping(value = "/{path}/do_miaosha")
    @ResponseBody
    public Result miaosha(Model model, SeckillUser user,
                          @RequestParam("goodsId") long goodsId,
                          @PathVariable("path") String path) {
        model.addAttribute("user", user);
        if (user == null) {
            return Result.fail(ErrorCode.SESSION_ERROR);
        }
        // 验证path
        boolean check = seckillService.checkPath(user, goodsId, path);
        if (!check) {
            return Result.fail(ErrorCode.REQUEST_ILLEGAL);
        }
        //内存标记，减少redis访问
        boolean over = localOverMap.get(goodsId);
        if (over) {
            return Result.fail(ErrorCode.SECKILL_OVER);
        }
        // 预减库存//10
        long stock = redisService.decr(GoodsKey.GET_MIAOSHA_GOODS_STOCK, "" + goodsId);
        if (stock < 0) {
            localOverMap.put(goodsId, true);
            return Result.fail(ErrorCode.SECKILL_OVER);
        }
        //判断是否已经秒杀到了
        SeckillOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
        if (order != null) {
            return Result.fail(ErrorCode.REPEATE_MIAOSHA);
        }
        //入队
        SeckillMessage mm = new SeckillMessage();
        mm.setUser(user);
        mm.setGoodsId(goodsId);
        sender.sendMiaoshaMessage(mm);
        //排队中
        return Result.success(0);

    }

    /**
     * orderId：成功
     * -1：秒杀失败
     * 0： 排队中
     */
    @GetMapping(value = "/result")
    @ResponseBody
    public Result miaoshaResult(Model model, SeckillUser user,
                                @RequestParam("goodsId") long goodsId) {
        model.addAttribute("user", user);
        if (user == null) {
            return Result.fail(ErrorCode.SESSION_ERROR);
        }
        long result = seckillService.getMiaoshaResult(user.getId(), goodsId);
        return Result.success(result);
    }

    /**
     * 自定义注解@AccessLimit ，表示5秒钟内最多请求5次，需要登录
     * 后去秒杀路径
     *
     * @param user       用户
     * @param goodsId    商品编号
     * @param verifyCode 验证码
     * @return 路径
     */
    @AccessLimit(seconds = 5, maxCount = 5)
    @GetMapping(value = "/path")
    @ResponseBody
    public Result getMiaoshaPath(SeckillUser user,
                                 @RequestParam("goodsId") long goodsId,
                                 @RequestParam(value = "verifyCode", defaultValue = "0") int verifyCode) {
        if (user == null) {
            return Result.fail(ErrorCode.SESSION_ERROR);
        }
        boolean check = seckillService.checkVerifyCode(user, goodsId, verifyCode);
        if (!check) {
            return Result.fail(ErrorCode.REQUEST_ILLEGAL);
        }
        String path = seckillService.createMiaoshaPath(user, goodsId);
        return Result.success(path);
    }


    @GetMapping(value = "/verifyCode")
    @ResponseBody
    public Result getMiaoshaVerifyCod(HttpServletResponse response, SeckillUser user,
                                      @RequestParam("goodsId") long goodsId) {
        if (user == null) {
            return Result.fail(ErrorCode.SESSION_ERROR);
        }
        try {
            BufferedImage image = seckillService.createVerifyCode(user, goodsId);
            OutputStream out = response.getOutputStream();
            ImageIO.write(image, "JPEG", out);
            out.flush();
            out.close();
            return null;
        } catch (Exception e) {
            log.error("【获取验证码异常】:", e);
            return Result.fail(ErrorCode.SECKILL_FAIL);
        }
    }
}
